﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;

namespace VisualRibbonEditor
{
	public partial class NewButtonDialog : UserControl, IDialog
	{
		private XElement _insertIntoGroup;

		const string PREFIX_DEFAULT = "Company Name";

		public NewButtonDialog(XElement group)
		{
			_insertIntoGroup = group;
			InitializeComponent();

			//set up ID;
			string prefix = MainWindow.CurrentSolution.GetIdPrefix() ?? string.Empty;

			if (string.IsNullOrEmpty(prefix))
			{
				//unable to determine what the "company" prefix is, so we have to allow the user to enter it.
				prefixTextbox.Visibility = System.Windows.Visibility.Visible;
				prefixTextbox.Text = PREFIX_DEFAULT;
			}
			else
			{
				//company prefix was determined by looking at the other buttons, so we can just display it.
				//(dont allow user to enter a different prefix into the prefixtextbox)
				prefixTextbox.Visibility = System.Windows.Visibility.Visible;
				prefixTextbox.Text = prefix;
			}

			string groupId = group.Attribute("Id").Value;
			if (groupId.StartsWith("Mscrm"))
			{
				groupId = groupId.Substring(5);
			}
			if (groupId.EndsWith("Controls"))
			{
				groupId = groupId.Substring(0, groupId.Length - 8);
			}
			if (!groupId.EndsWith("."))
			{
				groupId = groupId + ".";
			}
		 
			idPrefix.Text = groupId;
		}

		private void OKButton_Click(object sender, RoutedEventArgs e)
		{
			string id = GetValidId();
			if (id == null)
			{
				//invalid 
				return;
			}

			InsertAllNewButtonXml(id, IdTextbox.Text);

			if (DialogClosed != null)
			{
				DialogClosed(IdTextbox.Text);
			}
		}

		/// <summary>
		/// This does all the xml manipulation to create a new ribbon button.
		/// (in both the base xml file and the diff xml)
		/// </summary>
		private void InsertAllNewButtonXml(string newButtonId, string newButtonName)
		{
			XElement baseRoot = _insertIntoGroup.Ancestors().Last();
			XElement diffRoot = MainWindow.CurrentSolution.RibbonDiffXml;

			//insert button into base and into diff
			InsertButtonIntoBaseXmlGroup(newButtonId);
			InsertCustomActionIntoDiffXml(newButtonId, _insertIntoGroup.Attribute("Id").Value);

			//insert commands into base and into diff
			InsertCommandDefinition(baseRoot, newButtonId);
			InsertCommandDefinition(diffRoot, newButtonId);
			
			//insert labels into diff
			InsertButtonLabelsIntoDiffXml(newButtonId, newButtonName);
		}

		private string GetSequenceNumber()
		{
			int max = 0;
			foreach (var at in _insertIntoGroup.Descendants().Attributes("Sequence"))
			{
				int seqValue = 0;
				if (int.TryParse(at.Value, out seqValue))
				{
					if (seqValue > max)
					{
						max = seqValue;
					}
				}
			}
			max += 10; //either start at 10 or 10 above the currently highest sequence.
			return max.ToString();
		}

		/// <summary>
		/// inserts a button into the base xml 
		/// </summary>
		private void InsertButtonIntoBaseXmlGroup(string newButtonId)
		{
			XElement button = new XElement("Button");
			button.SetAttributeValue("Id", newButtonId);
			button.SetAttributeValue("Command", newButtonId + ".Command");
			button.SetAttributeValue("Sequence", GetSequenceNumber());

			string newButtonIdNoAllEntityToken = newButtonId.Replace("{!EntityLogicalName}", "EntityLogicalName");

			button.SetAttributeValue("LabelText", "$LocLabels:" + newButtonIdNoAllEntityToken + ".LabelText");
			button.SetAttributeValue("ToolTipTitle", "$LocLabels:" + newButtonIdNoAllEntityToken + ".ToolTipTitle");
			button.SetAttributeValue("ToolTipDescription", "$LocLabels:" + newButtonIdNoAllEntityToken + ".Description");
			
			button.SetAttributeValue("TemplateAlias", "isv");

			_insertIntoGroup.Add(button);		 
		}

		/// <summary>
		/// Inserts the CustomAction for the new button into the diff xml 
		/// </summary>
		private void InsertCustomActionIntoDiffXml(string newButtonId, string insertIntoGroupId)
		{
			XElement button = new XElement("Button");
			button.SetAttributeValue("Id", newButtonId);
			button.SetAttributeValue("Command", newButtonId + ".Command");
			button.SetAttributeValue("Sequence", GetSequenceNumber());

			string newButtonIdNoAllEntityToken = newButtonId.Replace("{!EntityLogicalName}", "EntityLogicalName");

			button.SetAttributeValue("LabelText", "$LocLabels:" + newButtonIdNoAllEntityToken + ".LabelText");
			button.SetAttributeValue("ToolTipTitle", "$LocLabels:" + newButtonIdNoAllEntityToken + ".ToolTipTitle");
			button.SetAttributeValue("ToolTipDescription", "$LocLabels:" + newButtonIdNoAllEntityToken + ".Description");

			button.SetAttributeValue("TemplateAlias", "isv");

			XElement CustomActions = MainWindow.CurrentSolution.RibbonDiffXml.Descendants("CustomActions").FirstOrDefault();
			XElement Group = CustomActions.Descendants("Controls").Where(x => x.Attribute("Id").Value == insertIntoGroupId).FirstOrDefault();
			if (Group != null)
			{
				//insert into a group thats in the diffxml
				Group.Add(button);
			}
			else
			{
				//insert into a group thats in the base xml
				XElement CustomAction = new XElement("CustomAction");
				CustomAction.SetAttributeValue("Id", newButtonId + ".CustomAction");
				CustomAction.SetAttributeValue("Location", insertIntoGroupId + "._children");
				CustomAction.SetAttributeValue("Sequence", "41");
				CustomActions.Add(CustomAction);

				XElement CommandUIDefinition = new XElement("CommandUIDefinition");
				CustomAction.Add(CommandUIDefinition);
				CommandUIDefinition.Add(button);
			}
		}

		/// <summary>
		/// Inserts a new CommandDefiniton element into given xml file for the new button
		/// </summary>
		private void InsertCommandDefinition(XElement xmlFileRoot, string newButtonId)
		{
			XElement CommandDefinitions = xmlFileRoot.Descendants("CommandDefinitions").FirstOrDefault();

			XElement CommandDefinition = new XElement("CommandDefinition");
			CommandDefinition.SetAttributeValue("Id", newButtonId + ".Command");
			CommandDefinition.Add(new XElement("EnableRules"));
			CommandDefinition.Add(new XElement("DisplayRules"));
			CommandDefinition.Add(new XElement("Actions"));
			//command will be blank (with no action or rules) at first.

			CommandDefinitions.Add(CommandDefinition);
		}

		/// <summary>
		/// Adds both the labelText and description labels to the LocLabels section of the diff xml
		/// </summary>
		private void InsertButtonLabelsIntoDiffXml(string newButtonId, string newButtonText)
		{
			XElement LocLabels = MainWindow.CurrentSolution.RibbonDiffXml.Descendants("LocLabels").FirstOrDefault();

			string newButtonIdNoAllEntityToken = newButtonId.Replace("{!EntityLogicalName}", "EntityLogicalName");

			AddLabel(LocLabels, newButtonIdNoAllEntityToken + ".LabelText", newButtonText);
			AddLabel(LocLabels, newButtonIdNoAllEntityToken + ".ToolTipTitle", newButtonText);
			AddLabel(LocLabels, newButtonIdNoAllEntityToken + ".Description", newButtonText + " Description");
		}

		/// <summary>
		/// Adds a single label in English to the LocLabels collection
		/// </summary>
		private void AddLabel(XElement LocLabelsElement, string id, string label)
		{
			XElement LocLabel = new XElement("LocLabel");
			LocLabel.SetAttributeValue("Id", id);
			LocLabelsElement.Add(LocLabel);

			XElement Titles = new XElement("Titles");
			LocLabel.Add(Titles);

			XElement Title = new XElement("Title");
			Title.SetAttributeValue("languagecode", Connection.CurrentConnection.BaseLanguage);
			Title.SetAttributeValue("description", label);
			Titles.Add(Title);
		}

		/// <summary>
		/// Validates the ID entered into the dialog and returns the ID if its valid.
		/// returns NULL if it is not valid.
		/// </summary>
		private string GetValidId()
		{

			if (string.IsNullOrEmpty(IdTextbox.Text))
			{
				MessageBox.Show("You must specify an ID for the new button");
				return null;
			}

			if (string.IsNullOrWhiteSpace(prefixTextbox.Text))
			{
				MessageBox.Show("You must specify a full ID, including a prefix (i.e., company or organization name).");
				return null;
			}

			string newId = string.Empty;
			newId += prefixTextbox.Text;			 
			newId += idPrefix.Text;
			newId += IdTextbox.Text;

			string validchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.{}!";
			foreach (char c in newId)
			{
				if (!validchars.Contains(c))
				{
					MessageBox.Show("Id must be alphanumeric or underscore, with no spaces.");
					return null;
				}
			}

			if (IdIsInUse(newId))
			{
				MessageBox.Show("The Id \"" + newId + "\" is already in use.");
				return null;
			}

			return newId;
		}


		/// <summary>
		/// Returns true if the ID given is already in use anywhere in the ribbon xml.
		/// </summary>
		private bool IdIsInUse(string id)
		{
			//check to see if the id or id.command is already used in the base xml file
			XElement baseRoot = _insertIntoGroup.Ancestors().Last();
			foreach (XAttribute att in baseRoot.Descendants().Attributes("Id"))
			{
				if (string.Compare(att.Value, id, StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
				if (string.Compare(att.Value, id + ".Command", StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
			}

			//check to see if the id (or the id plus any of the suffixes) is already used in the diff xml.
			XElement diffRoot = MainWindow.CurrentSolution.RibbonDiffXml;
			foreach (XAttribute att in diffRoot.Descendants().Attributes("Id"))
			{
				if (string.Compare(att.Value, id, StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
				if (string.Compare(att.Value, id + ".Command", StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
				if (string.Compare(att.Value, id + ".CustomAction", StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
				if (string.Compare(att.Value, id + ".LabelText", StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
				if (string.Compare(att.Value, id + ".ToolTipTitle", StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
				if (string.Compare(att.Value, id + ".Description", StringComparison.InvariantCultureIgnoreCase) == 0)
					return true;
			}

			return false;
		}

		/// <summary>
		/// user clicked the cancel button. Call DialogClose event with NULL to indicate a cancel.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void CancelButton_Click(object sender, RoutedEventArgs e)
		{
			if (DialogClosed != null)
			{
				DialogClosed(null);
			}
		}

		public event CloseDialogHandler DialogClosed;

		private void UserControl_Loaded(object sender, RoutedEventArgs e)
		{
			if (prefixTextbox.Text == PREFIX_DEFAULT)
			{
				prefixTextbox.Focus();
				prefixTextbox.SelectAll();
			}
			else
			{
				IdTextbox.Focus();
			}
		}

		private void IdTextbox_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Enter)
			{
				OKButton_Click(sender, new RoutedEventArgs());
			}
		}

		private void NewButtonDialog_KeyUp(object sender, KeyEventArgs e)
		{
			switch (e.Key) //
			{
				// ESC
				case Key.Escape:
					if (DialogClosed != null) DialogClosed(null);
					break;
				default:
					break;
			}
		}
	}

}
